weekling 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,47 @@
1
+ # encoding: UTF-8
2
+ =begin
3
+ Copyright Alexander E. Fischer <aef@raxys.net>, 2012
4
+
5
+ This file is part of Weekling.
6
+
7
+ Permission to use, copy, modify, and/or distribute this software for any
8
+ purpose with or without fee is hereby granted, provided that the above
9
+ copyright notice and this permission notice appear in all copies.
10
+
11
+ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
12
+ REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
13
+ FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
14
+ INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
15
+ LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
16
+ OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
17
+ PERFORMANCE OF THIS SOFTWARE.
18
+ =end
19
+
20
+ require 'rake'
21
+ require 'pathname'
22
+ require 'bundler/gem_tasks'
23
+ require 'yard'
24
+ require 'rspec/core/rake_task'
25
+
26
+ RSpec::Core::RakeTask.new
27
+
28
+ YARD::Rake::YardocTask.new('doc')
29
+
30
+ desc "Removes temporary project files"
31
+ task :clean do
32
+ %w{doc coverage pkg .yardoc .rbx}.map{|name| Pathname.new(name) }.each do |path|
33
+ path.rmtree if path.exist?
34
+ end
35
+
36
+ Pathname.glob('*.gem').each &:delete
37
+ Pathname.glob('**/*.rbc').each &:delete
38
+ end
39
+
40
+ desc "Opens an interactive console with the library loaded"
41
+ task :console do
42
+ require 'pry'
43
+ require 'weekling'
44
+ Pry.start
45
+ end
46
+
47
+ task :default => :spec
@@ -0,0 +1,45 @@
1
+ # encoding: UTF-8
2
+ =begin
3
+ Copyright Alexander E. Fischer <aef@raxys.net>, 2012
4
+
5
+ This file is part of Weekling.
6
+
7
+ Permission to use, copy, modify, and/or distribute this software for any
8
+ purpose with or without fee is hereby granted, provided that the above
9
+ copyright notice and this permission notice appear in all copies.
10
+
11
+ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
12
+ REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
13
+ FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
14
+ INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
15
+ LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
16
+ OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
17
+ PERFORMANCE OF THIS SOFTWARE.
18
+ =end
19
+
20
+ # Namespace for projects of Alexander E. Fischer <aef@raxys.net>.
21
+ #
22
+ # If you want to be able to simply type Example instead of Aef::Example to
23
+ # address classes in this namespace simply write the following before using the
24
+ # classes.
25
+ #
26
+ # @example Including the namespace
27
+ # include Aef
28
+ # @author Alexander E. Fischer
29
+ module Aef
30
+
31
+ # Namespace for components of the weekling gem
32
+ module Weekling
33
+
34
+ # The currently loaded library version
35
+ VERSION = '1.0.0'.freeze
36
+
37
+ end
38
+ end
39
+
40
+ require 'time'
41
+ require 'aef/weekling/year'
42
+ require 'aef/weekling/week'
43
+ require 'aef/weekling/week_day'
44
+ require 'aef/weekling/core_extensions/to_year'
45
+ require 'aef/weekling/core_extensions/to_week_and_week_day'
@@ -0,0 +1,28 @@
1
+ # encoding: UTF-8
2
+ =begin
3
+ Copyright Alexander E. Fischer <aef@raxys.net>, 2012
4
+
5
+ This file is part of Weekling.
6
+
7
+ Permission to use, copy, modify, and/or distribute this software for any
8
+ purpose with or without fee is hereby granted, provided that the above
9
+ copyright notice and this permission notice appear in all copies.
10
+
11
+ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
12
+ REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
13
+ FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
14
+ INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
15
+ LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
16
+ OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
17
+ PERFORMANCE OF THIS SOFTWARE.
18
+ =end
19
+
20
+ module Aef
21
+ module Weekling
22
+
23
+ # Namespace for extension modules for Ruby's core classes.
24
+ module CoreExtensions
25
+
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,41 @@
1
+ # encoding: UTF-8
2
+ =begin
3
+ Copyright Alexander E. Fischer <aef@raxys.net>, 2012
4
+
5
+ This file is part of Weekling.
6
+
7
+ Permission to use, copy, modify, and/or distribute this software for any
8
+ purpose with or without fee is hereby granted, provided that the above
9
+ copyright notice and this permission notice appear in all copies.
10
+
11
+ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
12
+ REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
13
+ FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
14
+ INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
15
+ LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
16
+ OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
17
+ PERFORMANCE OF THIS SOFTWARE.
18
+ =end
19
+
20
+ module Aef
21
+ module Weekling
22
+ module CoreExtensions
23
+
24
+ # This module allows date-like objects to be extended to support
25
+ # to_year, to_week and to_week_day.
26
+ module ToWeekAndWeekDay
27
+
28
+ # Constructs an Aef::Weekling::Week from the object.
29
+ def to_week
30
+ Week.new(self)
31
+ end
32
+
33
+ # Constructs an Aef::Weekling::Week::Day from the object.
34
+ def to_week_day
35
+ WeekDay.new(self)
36
+ end
37
+
38
+ end
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,36 @@
1
+ # encoding: UTF-8
2
+ =begin
3
+ Copyright Alexander E. Fischer <aef@raxys.net>, 2012
4
+
5
+ This file is part of Weekling.
6
+
7
+ Permission to use, copy, modify, and/or distribute this software for any
8
+ purpose with or without fee is hereby granted, provided that the above
9
+ copyright notice and this permission notice appear in all copies.
10
+
11
+ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
12
+ REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
13
+ FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
14
+ INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
15
+ LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
16
+ OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
17
+ PERFORMANCE OF THIS SOFTWARE.
18
+ =end
19
+
20
+ module Aef
21
+ module Weekling
22
+ module CoreExtensions
23
+
24
+ # This module allows integer-like objects to be extended to support
25
+ # to_year.
26
+ module ToYear
27
+
28
+ # Constructs an Aef::Weekling::Year from the object.
29
+ def to_year
30
+ Year.new(self)
31
+ end
32
+
33
+ end
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,373 @@
1
+ # encoding: UTF-8
2
+ =begin
3
+ Copyright Alexander E. Fischer <aef@raxys.net>, 2012
4
+
5
+ This file is part of Weekling.
6
+
7
+ Permission to use, copy, modify, and/or distribute this software for any
8
+ purpose with or without fee is hereby granted, provided that the above
9
+ copyright notice and this permission notice appear in all copies.
10
+
11
+ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
12
+ REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
13
+ FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
14
+ INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
15
+ LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
16
+ OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
17
+ PERFORMANCE OF THIS SOFTWARE.
18
+ =end
19
+
20
+ require 'aef/weekling'
21
+
22
+ module Aef
23
+ module Weekling
24
+
25
+ # Immutable object representing a calendar week (according to ISO 8601).
26
+ class Week
27
+ include Comparable
28
+
29
+ # Regular expression for week extraction from strings.
30
+ # @private
31
+ PARSE_PATTERN = /(0|-?\d+)-W(0[1-9]|(?:1|2|3|4)\d|5(?:0|1|2|3))/
32
+
33
+ class << self
34
+ # Initializes the current week.
35
+ #
36
+ # @return [Aef::Weekling::Week] the current week
37
+ def today
38
+ today = Date.today
39
+
40
+ new(today.year, today.cweek)
41
+ end
42
+
43
+ alias now today
44
+
45
+ # Parses the first week out of a string.
46
+ #
47
+ # @note Looks for patterns like this:
48
+ # 2011-W03
49
+ # @param [String] string a string containing a week representation
50
+ # @return [Aef::Weekling::Week] the week parsed from input
51
+ # @raise [ArgumentError] if pattern cannot be found
52
+ def parse(string)
53
+ if sub_matches = PARSE_PATTERN.match(string.to_s)
54
+ original, year, index = *sub_matches
55
+ new(year.to_i, index.to_i)
56
+ else
57
+ raise ArgumentError, 'No week found for parsing'
58
+ end
59
+ end
60
+ end
61
+
62
+ # @return [Aef::Weekling::Year] the year the week is part of
63
+ attr_reader :year
64
+
65
+ # @return [Integer] the number of the week in its year
66
+ attr_reader :index
67
+
68
+ # @overload initialize(week)
69
+ # Initialize by a week-like object.
70
+ # @param [Aef::Weekling::Week] week a week-like object
71
+ #
72
+ # @overload initialize(date)
73
+ # Initialize by a date-like object.
74
+ # @param [Date, DateTime, Time] date a date-like object
75
+ #
76
+ # @overload initialize(year, index)
77
+ # Initialize by year and week number.
78
+ # @param [Integer, Aef::Weekling::Year] year a year
79
+ # @param [Integer] index a week index
80
+ def initialize(*arguments)
81
+ case arguments.count
82
+ when 1
83
+ object = arguments.first
84
+ if [:year, :index].all?{|method_name| object.respond_to?(method_name) }
85
+ @year = object.year.to_year
86
+ @index = object.index.to_i
87
+ elsif object.respond_to?(:to_date)
88
+ date = object.to_date
89
+ @year = Year.new(date.year)
90
+ @index = date.cweek
91
+ else
92
+ raise ArgumentError, 'A single argument must either respond to #year and #index or to #to_date'
93
+ end
94
+ when 2
95
+ year, index = *arguments
96
+ @year = Year.new(year)
97
+ @index = index.to_i
98
+ else
99
+ raise ArgumentError, "wrong number of arguments (#{arguments.count} for 1..2)"
100
+ end
101
+
102
+ if not (1..52).include?(@index)
103
+ if @index == 53
104
+ if @year.week_count == 52
105
+ raise ArgumentError, "Index #{@index} is invalid. Year #{@year} has only 52 weeks"
106
+ end
107
+ else
108
+ raise ArgumentError, "Index #{@index} is invalid. Index can never be lower than 1 or higher than 53"
109
+ end
110
+ end
111
+
112
+ end
113
+
114
+ # Represents a week as String in ISO 8601 format.
115
+ #
116
+ # @example Output of the 13th week in 2012
117
+ # Aef::Weekling::Week.new(2012, 13).to_s
118
+ # # => "2012-W13"
119
+ #
120
+ # @return [String] a character representation of the week
121
+ def to_s
122
+ "#{'%04i' % year}-W#{'%02i' % index}"
123
+ end
124
+
125
+ # Represents a week as String for debugging.
126
+ #
127
+ # @example Output of the 13th week in 2012
128
+ # Aef::Weekling::Week.new(2012, 13).inspect
129
+ # # => "#<Aef::Weekling::Week: 2012-W13>"
130
+ #
131
+ # @return [String] a character representation for debugging
132
+ def inspect
133
+ "#<#{self.class.name}: #{to_s}>"
134
+ end
135
+
136
+ # @return [Aef::Weekling::Week] self reference
137
+ def to_week
138
+ self
139
+ end
140
+
141
+ # @param [Aef::Weekling::Week] other a week-like object to be compared
142
+ # @return [true, false] true if other lies in the same year and has the
143
+ # same index
144
+ def ==(other)
145
+ other_week = self.class.new(other)
146
+
147
+ year == other_week.year and index == other_week.index
148
+ end
149
+
150
+ # @param [Aef::Weekling::Week] other a week object to be compared
151
+ # @return [true, false] true if other lies in the same year, has the same
152
+ # index and is of the same or a descending class
153
+ def eql?(other)
154
+ other.is_a?(self.class) and self == other
155
+ end
156
+
157
+ # @return [see Array#hash] identity hash for hash table usage
158
+ def hash
159
+ [year, index].hash
160
+ end
161
+
162
+ # Compares the week with another to determine its relative position.
163
+ #
164
+ # @param [Aef::Weekling::Week] other a week-like object to be compared
165
+ # @return [-1, 0, 1] -1 if other is greater, 0 if other is equal and 1 if
166
+ # other is lesser than self
167
+ def <=>(other)
168
+ other_week = self.class.new(other)
169
+
170
+ year_comparison = year <=> other_week.year
171
+
172
+ return index <=> other_week.index if year_comparison == 0
173
+ return year_comparison
174
+ end
175
+
176
+ # Finds the following week.
177
+ #
178
+ # @example The week after some other week
179
+ # some_week = Aef::Weekling::Week.new(2012, 5)
180
+ # some_week.next
181
+ # # => #<Aef::Weekling::Week: 2012-W06>
182
+ #
183
+ # @example The week after the last week of a year
184
+ # last_week_in_year = Aef::Weekling::Week.new(2012, 52)
185
+ # last_week_in_year.next
186
+ # # => #<Aef::Weekling::Week: 2013-W01>
187
+ #
188
+ # @return [Aef::Weekling::Week] the following week
189
+ def next
190
+ if index < 52
191
+ self.class.new(year, index + 1)
192
+ elsif year.week_count == 53 and index == 52
193
+ self.class.new(year, index + 1)
194
+ else
195
+ self.class.new(year.next, 1)
196
+ end
197
+ end
198
+
199
+ alias succ next
200
+
201
+ # Find the previous week.
202
+ #
203
+ # @example The week before some other week
204
+ # some_week = Aef::Weekling::Week.new(2012, 5)
205
+ # some_week.previous
206
+ # # => #<Aef::Weekling::Week: 2012-W04>
207
+ #
208
+ # @example The week before the first week of a year
209
+ # first_week_in_year = Aef::Weekling::Week.new(2016, 1)
210
+ # first_week_in_year.previous
211
+ # # => #<Aef::Weekling::Week: 2015-W53>
212
+ #
213
+ # @return [Aef::Weekling::Week] the previous week
214
+ def previous
215
+ if index > 1
216
+ self.class.new(year, index - 1)
217
+ elsif year.previous.week_count == 53
218
+ self.class.new(year.previous, 53)
219
+ else
220
+ self.class.new(year.previous, 52)
221
+ end
222
+ end
223
+
224
+ alias pred previous
225
+
226
+ # Adds weeks to the week.
227
+ #
228
+ # @example 28 weeks after 2007-W01
229
+ # Aef::Weekling::Week.new(2007, 1) + 28
230
+ # # => #<Aef::Weekling::Week: 2007-W29>
231
+ #
232
+ # @param [Integer] other number of weeks to add
233
+ # @return [Aef::Weekling::Week] the resulting week
234
+ def +(other)
235
+ result = self
236
+ number = other.to_i
237
+
238
+ number.abs.times do
239
+ if number < 0
240
+ result = result.previous
241
+ else
242
+ result = result.next
243
+ end
244
+ end
245
+
246
+ result
247
+ end
248
+
249
+ # Subtracts weeks from the week.
250
+ #
251
+ # @example 3 weeks before 2000-W03
252
+ # Aef::Weekling::Week.new(2000, 3) - 3
253
+ # # => #<Aef::Weekling::Week: 1999-W52>
254
+ #
255
+ # @param [Integer] other number of weeks to subtract
256
+ # @return [Aef::Weekling::Week] the resulting week
257
+ def -(other)
258
+ self + -other.to_i
259
+ end
260
+
261
+ # Returns a range of weeks beginning with self and ending with the first
262
+ # following week with the given index.
263
+ #
264
+ # @example End index higher than start index
265
+ # Aef::Weekling::Week.new(2012, 35).until_index(50)
266
+ # # => #<Aef::Weekling::Week: 2012-W35>..#<Aef::Weekling::Week: 2012-W50>
267
+ #
268
+ # @example End index lower or equal than start index
269
+ # Aef::Weekling::Week.new(2012, 35).until_index(11)
270
+ # # => #<Aef::Weekling::Week: 2012-W35>..#<Aef::Weekling::Week:2013-W11>
271
+ #
272
+ # @param [Integer] end_index the number of the last week in the result
273
+ # @return [Range<Aef::Weekling::Week>] range from self to the first
274
+ # following week with the given index
275
+ def until_index(end_index)
276
+ if end_index <= index
277
+ self .. self.class.new(year.next, end_index)
278
+ else
279
+ self .. self.class.new(year, end_index)
280
+ end
281
+ end
282
+
283
+ # States if the week's index is odd.
284
+ #
285
+ # @return [true, false] true if the week is odd
286
+ def odd?
287
+ @index.odd?
288
+ end
289
+
290
+ # States if the week's index is even.
291
+ #
292
+ # @return [true, false] true if the week is even
293
+ def even?
294
+ @index.even?
295
+ end
296
+
297
+ # @overload day(index)
298
+ # Returns a week-day by given index.
299
+ # @param [Integer] the index between 1 for monday and 7 for sunday
300
+ # @overload day(symbol)
301
+ # Returns a week-day by given symbol.
302
+ # @param [Symbol] the English name of the day in lowercase
303
+ # @return [Aef::Weekling::WeekDay] the specified day of the week
304
+ def day(index_or_symbol)
305
+ WeekDay.new(self, index_or_symbol)
306
+ end
307
+
308
+ # Returns the week's monday.
309
+ #
310
+ # @return [Aef::Weekling::WeekDay] monday of the week
311
+ def monday
312
+ day(:monday)
313
+ end
314
+
315
+ # Returns the week's tuesday.
316
+ #
317
+ # @return [Aef::Weekling::WeekDay] tuesday of the week
318
+ def tuesday
319
+ day(:tuesday)
320
+ end
321
+
322
+ # Returns the week's wednesday.
323
+ #
324
+ # @return [Aef::Weekling::WeekDay] wednesday of the week
325
+ def wednesday
326
+ day(:wednesday)
327
+ end
328
+
329
+ # Returns the week's thursday.
330
+ #
331
+ # @return [Aef::Weekling::WeekDay] thursday of the week
332
+ def thursday
333
+ day(:thursday)
334
+ end
335
+
336
+ # Returns the week's friday.
337
+ #
338
+ # @return [Aef::Weekling::WeekDay] friday of the week
339
+ def friday
340
+ day(:friday)
341
+ end
342
+
343
+ # Returns the week's saturday.
344
+ #
345
+ # @return [Aef::Weekling::WeekDay] saturday of the week
346
+ def saturday
347
+ day(:saturday)
348
+ end
349
+
350
+ # Returns the week's sunday.
351
+ #
352
+ # @return [Aef::Weekling::WeekDay] sunday of the week
353
+ def sunday
354
+ day(:sunday)
355
+ end
356
+
357
+ # Returns the week's saturday and sunday in an Array.
358
+ #
359
+ # @return [Array<Aef:Weekling::WeekDay>] the saturday and sunday of a week
360
+ def weekend
361
+ [saturday, sunday]
362
+ end
363
+
364
+ # Returns a range from monday to sunday.
365
+ #
366
+ # @return [Range<Aef::Weekling::WeekDay>] the days of the week
367
+ def days
368
+ monday..sunday
369
+ end
370
+
371
+ end
372
+ end
373
+ end